<template>
  <div
    :class="scrollClass"
    :style="scrollStyleCom"
    ref="scroll"
    @wheel="handleWheel"
    @mouseenter="handleRefresh"
  >
  <!-- 滚动内容容器 -->
    <div :class="prefixCls + '-content'" ref="content">
      <slot></slot>
    </div>
    <!-- 滚动条 -->
    <div
      v-if="!disableBar"
      v-show="isScrollable"
      :class="barClass"
      :style="barStyleCom"
      ref="bar"
      @mouseenter="handleBarMouseEnter"
      @mouseleave="handleBarMouseLeave"
    >
      <div
        :class="loadClass"
        :style="loadStyleCom"
        ref="load"
        @mousedown="handleLoadMouseDown"
      ></div>
    </div>
  </div>
</template>
<script>
import { oneOf, preventDefault } from "utils/index.js";
import { throttle } from "utils/tool";
const prefixCls = "vbestui-scroll";
export default {
  name: "BScroll",
  props: {
    height: {
      type: String,
      default: "",
    },
    theme: {
      validator(value) {
        return oneOf(value, ["light", "dark"]);
      },
      default: "light",
    },
    /**滑轮滚动速度 */
    speed: {
      type: Number,
      default: 30,
    },
    /**上拉加载更多、下拉刷新触发延迟，在这个时间内不会触发 */
    pullingDelay: {
      type: Number,
      default: 200,
    },
    /**new 触底事件距离界限，当可滚动距离小于这个距离时触发pullingUp */
    pullingDistance: {
      type: Number,
      default: 50,
    },
    /**是否禁用滚动 :禁用所有滚动*/
    isDisable: {
      type: Boolean,
      default: false,
    },
    /**禁用mousewheel */
    disableWheel: {
      type: Boolean,
      default: false,
    },
    /**禁用滚动条 true禁用*/
    disableBar: {
      type: Boolean,
      default: false,
    },
    /**滚动条滑块是否是圆角样式 */
    round: {
      type: Boolean,
      default: false,
    },
    /**滚动条轨道背景颜色 */
    trackColor: {
      type: String,
      default: "",
    },
    trackActiveColor: {
      type: String,
      default: "",
    },
    /**滚动条滑块颜色 */
    thumbColor: {
      type: String,
      default: "",
    },
    thumbActiveColor: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      prefixCls: prefixCls,
      barStyle: {},
      loadStyle: {},
      contentStyle: {},
      scrollStyle: {},
      loadHeight: 0, //滚动条小球高度
      isScroll: false, //是否在滚动状态
      scrollY: 0, //滚动条Y轴可滚动距离
      loadDistanceY: 0, //鼠标点击时鼠标据load顶部距离
      scrollPercent: 0, //页面滚动百分比，
      offsetHeight: 0, //页面可滚动的距离
      wheelDeltaY: 0, //Y轴已滚动位置
      timer: null, //在上拉刷新、上拉加载更多使使用延时器，降低其触发频率
    };
  },
  computed: {
    scrollClass() {
      return [this.prefixCls];
    },
    scrollStyleCom() {
      return {
        height: this.height,
      };
    },
    barClass() {
      return [
        `${this.prefixCls + "-bar"}`,
        `${this.prefixCls + "-bar-" + this.theme}`,
      ];
    },
    barStyleCom() {
      return {
        background: this.trackColor,
      };
    },
    loadClass() {
      return [
        `${this.prefixCls + "-bar-load"}`,
        `${this.prefixCls + "-bar-load-" + this.theme}`,
        this.isDisable ? `${this.prefixCls + "-bar-load-disable"}` : "",
      ];
    },
    loadStyleCom() {
      return {
        top: this.getLoadOffset + "px",
        height: this.loadHeight + "px",
        background: this.thumbColor,
        borderRadius: this.round ? "8px" : "",
      };
    },
    /**根据滚动百分比计算scroll-bar位置 */
    getLoadOffset() {
      this.scrollPercent = Math.abs(this.wheelDeltaY) / this.offsetHeight;
      let offsetY = this.scrollPercent * this.scrollY;
      offsetY = offsetY < 0 ? 0 : offsetY;
      offsetY = offsetY > this.scrollY ? this.scrollY : offsetY;
      return offsetY;
    },
    /**只要有一个自定义颜色，就覆盖默认主题 */
    isSetBar() {
      return (
        this.trackColor != "" &&
        this.trackActiveColor != "" &&
        this.thumbColor != "" &&
        this.thumbActiveColor
      );
    },
    /**是否可滚动，不可滚动时不显示滚动条
     * 根据页面可滚动距离判断是否可滚动  true可滚动，false不可滚动
     */
    isScrollable() {
      let isScrollable = this.offsetHeight > 0 ? true : false;
      return isScrollable;
    },
    /**获取当前可滚动距离：
     * 页面可滚动距离 - 以滚动距离
     */
    getPullingDistance() {
      if (this.offsetHeight == 0 || this.wheelDeltaY == 0) return false;
      return this.offsetHeight - Math.abs(this.wheelDeltaY);
    },
  },
  methods: {
    /**暴露在外部的函数 */

    /**滚动到指定位置
     * @param y 要滚动的y轴位置
     * @param duration 滚动动画时间，单位s
     * @param judge 是否进行滚动区间判断，false不判断
     */
    scrollTo(y, duration = 0, judge = true) {
      if (this.isDisable) return;
      this.wheelDeltaY = y;

      if (judge) this.scrollRange();

      this.scrollPercent = this.wheelDeltaY / this.offsetHeight;
      this.setTransform(this.contentStyle, 0, this.wheelDeltaY, duration);
    },

    /**更新scroll，重新计算滚动量，滚动条滚动距离，滚动条高度 */
    refresh() {
      this.$nextTick(()=>{
        /**可滚动区间 :有bug初始化获取不到offsetHeight*/
      if(this.$refs.content)this.offsetHeight = this.$refs.content.offsetHeight - this.$refs.scroll.offsetHeight;

      /**根据 滚动条高度/内容高度 计算滚动条高度 */
      if (this.isScrollable&&this.$refs.scroll) {
        this.loadHeight =
          (this.$refs.scroll.offsetHeight / this.$refs.content.offsetHeight) *
          this.$refs.scroll.offsetHeight;
        /**设置load最少高度 */
        this.loadHeight = this.loadHeight < 25 ? 25 : this.loadHeight;
      }
      /**滚动条可滚动距离 */
      this.scrollY = this.$refs.scroll&&this.$refs.scroll.offsetHeight - this.loadHeight;
      })
    },

    /**内部函数，不可外部调用 */
    handleWheel(e) {
      /**content内容高度小于scroll高度，不能滚动 */
      if (!this.isScrollable) return;
      /**是否禁用mousewheel:禁用不执行 */
      if (this.disableWheel) return;
      if (this.isDisable) return;

      e = e || window.event;
      let wheelDeltaY = 0;
      /**对火狐进行处理
       * 火狐滑轮滚动方向和谷歌IE相反，统一滚动方向和大小。
       */
      if (window.navigator.userAgent.indexOf("Firefox") !== -1) {
        wheelDeltaY = (e.wheelDeltaY || e.deltaY) * -50;
      } else {
        wheelDeltaY += e.wheelDeltaY || e.deltaY;
      }
      /**滚动速度 */
      if (wheelDeltaY > 0) {
        this.wheelDeltaY += this.speed;
      } else {
        this.wheelDeltaY -= this.speed;
      }

      /**滚动区间校验 */
      this.scrollRange();
      /**计算滚动比例 */
      this.scrollPercent = Math.abs(this.wheelDeltaY / this.offsetHeight);
      this.setTransform(this.contentStyle, 0, this.wheelDeltaY, 0);
      /**禁用浏览器滚动 */
      preventDefault();
      /**wheel事件 */
      this.$emit("wheel", this.wheelDeltaY);
      this.$emit("scroll", this.wheelDeltaY);
    },
    /**滚动区间校验 */
    scrollRange() {
      this.wheelDeltaY = this.wheelDeltaY > 0 ? 0 : this.wheelDeltaY;
      this.wheelDeltaY =
        this.wheelDeltaY < -this.offsetHeight
          ? -this.offsetHeight
          : this.wheelDeltaY;
    },
    /**load滚动 */
    handleLoadMouseDown(e) {
      if (this.isDisable) return;

      document.addEventListener("mousemove", this.handleLoadMouseMove);
      document.addEventListener("mouseup", this.handleLoadMouseUp);
      this.isScroll = true;
      this.enterBarStyle();

      /**获取load窗口坐标 */
      let { y } = this.$refs.load.getBoundingClientRect();
      this.loadDistanceY = e.clientY - y;

      /**禁用浏览器复制 */
      document.body.style.userSelect = "none";

      /**开始mousemove滚动 */
      this.$emit("moveStart", this.wheelDeltaY);
    },

    handleLoadMouseMove(e) {
      /**获取滚动条视图窗口 */
      let { y } = this.$refs.scroll.getBoundingClientRect();
      let moveY = e.clientY - y - this.loadDistanceY;
      moveY = moveY > this.scrollY ? this.scrollY : moveY;
      moveY = moveY < 0 ? 0 : moveY;
      this.scrollPercent = Math.abs(moveY / this.scrollY);
      this.wheelDeltaY = -this.scrollPercent * this.offsetHeight;
      this.setTransform(
        this.contentStyle,
        0,
        -this.scrollPercent * this.offsetHeight,
        0
      );
      this.loadStyle.top = moveY + "px";

      /**正在使用mousemove滚动 */
      this.$emit("moving", this.wheelDeltaY);
      this.$emit("scroll", this.wheelDeltaY);
    },
    handleLoadMouseUp(e) {
      this.isScroll = false;
      this.leaveBarStyle();
      document.removeEventListener("mousemove", this.handleLoadMouseMove);
      document.removeEventListener("mouseup", this.handleLoadMouseUp);

      document.body.style.userSelect = "initial";

      this.$emit("moveEnd", this.wheelDeltaY);
    },
    /**鼠标进入scroll区域，主动触发刷新 */
    handleRefresh() {
      /**如果禁用滚动或者页面可滚动距离过小不刷新 */
      if (this.isDisable || !this.isScrollable) return;
      // this.$nextTick(() => {
        this.refresh();
      // });
    },
    /**鼠标进入滚动条 */
    handleBarMouseEnter() {
      this.enterBarStyle();
    },
    /**鼠标离开滚动条 */
    handleBarMouseLeave() {
      if (this.isScroll) return;
      this.leaveBarStyle();
    },
    setTransform(objStyle, x = 0, y = 0, duration = 0) {
      objStyle.transform = `translate(${x}px, ${y}px)`;
      objStyle["-ms-transform"] = `translate(${x}px, ${y}px)`; /* IE 9 */
      objStyle["-moz-transform"] = `translate(${x}px, ${y}px)`; /* Firefox */
      objStyle["-webkit-transform"] = `translate(${x}px, ${y}px)`; /* Safari 和 Chrome */
      objStyle["-o-transform"] = `translate(${x}px, ${y}px)`; /* Opera */
      objStyle.transition = `all ${duration}s`;
    },
    // 初始化滚动条
    initScroll() {
      this.$nextTick().then(() => {
        this.barStyle = this.$refs.bar && this.$refs.bar.style;
        this.loadStyle = this.$refs.load && this.$refs.load.style;
        this.contentStyle = this.$refs.content && this.$refs.content.style;
        this.scrollStyle = this.$refs.scroll && this.$refs.scroll.style;
      });
      
        /**初始化更新滚动条 */
        this.refresh();
    },
    enterBarStyle() {
      if (this.isDisable) return;
      /**较浅的 light load背景色:#a6a6a6 */
      this.theme == "light"
        ? this.setBarStyle("14px", "var(--bg-color)", "#a8a8a8")
        : this.setBarStyle("14px", "#1e1e1e", "#4f4f4f");
      if (!this.isSetBar || this.isSetBar) {
        this.setBarStyle(
          "14px",
          this.trackActiveColor || this.trackColor,
          this.thumbActiveColor || this.thumbColor
        );
      }
      this.loadStyle.borderRadius = this.round ? "14px" : "";
      // this.scrollStyle.paddingRight = "14px";//version0.1.9 取消鼠标进入样式
    },
    leaveBarStyle() {
      this.theme == "light"
        ? this.setBarStyle("8px", "transparent", "var(--bg-color)")
        : this.setBarStyle("8px", "#1e1e1e", "#4f4f4f");
      if (!this.isSetBar || this.isSetBar) {
        this.setBarStyle("8px", this.trackColor, this.thumbColor);
      }
      this.loadStyle.borderRadius = this.round ? "8px" : "";
    },
    /**设置滚动条样式
     * @param width 滚动条宽度
     * @param barBgColor 滚动条背景颜色
     * @param loadBgColor 滚动条小球背景颜色
     */
    setBarStyle(width, barBgColor, loadBgColor) {
      this.barStyle.width = width;
      this.barStyle.backgroundColor = barBgColor;
      this.loadStyle.backgroundColor = loadBgColor;
    },
  },
  created() {
    this.initScroll();
    /**监听页面大小 */
    window.addEventListener("resize", throttle(this.refresh, 500));
  },
  watch: {
    getPullingDistance() {
      /**对触底事件进行处理，使其在200ms内不处发
       * 设置一个变量，在pull事件时不能moving
       */
      /**触顶 */
      if (Math.abs(this.wheelDeltaY) < this.pullingDistance) {
        if (this.timer) clearTimeout(this.timer);
        this.timer = setTimeout(() => {
          this.$emit("pullingDown");
        }, this.pullingDelay);
      } else if (this.getPullingDistance < this.pullingDistance) {
        if (this.timer) clearTimeout(this.timer);
        this.timer = setTimeout(() => {
          this.$emit("pullingUp");
          this.refresh();
        }, this.pullingDelay);
      }
    },
    /**滚动元素增加时重新计算bar样式 */
    offsetHeight() {
      this.refresh();
    },
  },
};
</script>